#ifndef __ctkPimpl_h
#define __ctkPimpl_h

// Qt includes
#include <QtGlobal>

#define CTK_CONSTRUCTOR_NO_ARG_CXX(PUB)  \
	PUB::PUB()                             \
{                                    \
	CTK_INIT_PRIVATE(PUB);               \
}

#define CTK_CONSTRUCTOR_1_ARG_CXX(PUB, _ARG1)   \
	PUB::PUB(_ARG1 _parent) :                     \
	Superclass( _parent )                       \
{                                           \
	CTK_INIT_PRIVATE(PUB);                      \
}

#define CTK_SET_CXX(PUB, _TYPE, _NAME, _VARNAME)    \
	void PUB::_NAME(_TYPE var)                        \
{                                                 \
	ctk_d.ref()._VARNAME =  var;                    \
}

#define CTK_GET_CXX(PUB, _TYPE, _NAME, _VARNAME)   \
	_TYPE PUB::_NAME()const                          \
{                                                \
	return ctk_d.ref()._VARNAME;                   \
}

#define CTK_DECLARE_PRIVATE(PUB)                                          \
	friend class PUB##Private; ctkPrivateInterface<PUB, PUB##Private> ctk_d;  \
	typedef PUB Self;

#define CTK_DECLARE_PUBLIC(PUB) friend class PUB;

#define CTK_INIT_PRIVATE(PUB) ctk_d.setPublic(this)

#define CTK_D(PUB) PUB##Private* d = ctk_d()
#define CTK_D_REF(PUB) PUB##Private& d = ctk_d.ref()

#define CTK_P(PUB) PUB* p = ctk_p()
#define CTK_P_REF(PUB) PUB& p = ctk_p_ref()

#ifdef CTK_DOXYGEN_RUN

ctkPrivate<PUB>* ctk_d();

const ctkPrivate<PUB>* ctk_d();

PUB& ctk_p_ref();
PUB* ctk_p();

const PUB& ctk_p_ref();
const PUB* ctk_p();
#endif

template <typename PUB>
class ctkPrivate
{
public:
	virtual ~ctkPrivate(){}
	inline void CTK_setPublic(PUB* pub)
	{
		Q_ASSERT(pub);
		ctk_p_ptr = pub;
	}

protected:
	inline PUB& ctk_p_ref()
	{
		Q_ASSERT(this->ctk_p_ptr);
		return *this->ctk_p_ptr;
	}
	inline const PUB& ctk_p_ref() const
	{
		Q_ASSERT(this->ctk_p_ptr);
		return *this->ctk_p_ptr;
	}

	inline PUB* ctk_p()
	{
		Q_ASSERT(this->ctk_p_ptr);
		return this->ctk_p_ptr;
	}
	inline const PUB* ctk_p() const
	{
		Q_ASSERT(this->ctk_p_ptr);
		return this->ctk_p_ptr;
	}

private:
	PUB* ctk_p_ptr;
};

template <typename PUB, typename PVT>
class ctkPrivateInterface
{
	friend class ctkPrivate<PUB>;
public:
	ctkPrivateInterface()
	{
		this->pvt = new PVT;
	}
	~ctkPrivateInterface()
	{
		delete this->pvt;
	}

	inline void setPublic(PUB* pub)
	{
		Q_ASSERT(pub);
		this->pvt->CTK_setPublic(pub);
	}
	inline PVT& ref()
	{
		return *static_cast<PVT*>(this->pvt);
	}
	inline const PVT& ref() const
	{
		return *static_cast<PVT*>(this->pvt);
	}
	inline PVT* operator()()
	{
		return static_cast<PVT*>(this->pvt);
	}
	inline const PVT* operator()() const
	{
		return static_cast<PVT*>(this->pvt);
	}
private:
	ctkPrivate<PUB>* pvt;
};

#endif
